﻿@using StackExchange.Elastic
@using StackExchange.Opserver.Data.Dashboard
@using StackExchange.Opserver.Data.Elastic
@using StackExchange.Opserver.Views.Elastic
@model DashboardModel
@{
    var knownClusters = Model.Clusters.Where(c => c.Nodes.Data != null && c.Nodes.Data.Nodes != null && c.Nodes.Data.Nodes.Any());
    var unknownClusters = Model.Clusters.Where(c => c.Nodes.Data == null || c.Nodes.Data.Nodes == null || !c.Nodes.Data.Nodes.Any());
}
@helper CommonVersionHeader(ElasticCluster c)
{
    var nodes = c.Nodes.SafeData(true).Nodes;
    
    if (nodes != null && nodes.Any())
    {
        Version minVersion = nodes.Min(n => n.Version),
            maxVersion = nodes.Max(n => n.Version);
        var versionString = minVersion == maxVersion ? minVersion.ToString() : (minVersion + "-" + maxVersion);
        @:- Version @versionString
    }
}
<div class="node-group" data-name="elastic-overview">
    @foreach (var c in unknownClusters)
    {
        <table class="node-dashboard cluster-dashboard striped-dashboard">
            <tbody>
                <tr class="category-row">
                    <th colspan="12">
                        <h3>
                            @c.IconSpan() @(((ISearchableNode)c).DisplayName)
                            <span class="cluster-info">(unknown)</span>
                        </h3>
                    </th>
                </tr>
                <tr>
                    <th></th>
                    <th>Node</th>
                    <th>Healthy</th>
                    <th>Error</th>
                    <th>As of</th>
                </tr>
            </tbody>
            <tbody>
                @foreach (var n in c.ConnectionManager.AllNodes)
                {
                    <tr class="@(MonitorStatus.Critical.RowClass())">
                        <td>@(MonitorStatus.Critical.IconSpan())</td>
                        <td>@n.Endpoint</td>
                        <td>@n.IsHealthy</td>
                        <td title="@(n.LastError != null ? n.LastError.Message : null)">@(n.LastError != null ? n.LastError.Message.TruncateWithEllipsis(60) : null)</td>
                        <td>@((n.LastPing ?? n.LastSeen).ToRelativeTimeSpan())</td>
                    </tr>
                }
            </tbody>
        </table>
    }
    @foreach (var c in knownClusters)
    {
        var ss = c.ShardStates;
        var hs = c.HealthStatus.SafeData(true);
        var ns = c.Nodes.SafeData(true);
        <table class="node-dashboard cluster-dashboard striped-dashboard">
            <tbody>
                <tr class="category-row">
                    <th colspan="12">
                        <h3>
                            @hs.IconSpan() @c.Name
                            <span class="cluster-info">
                                (@ns.Nodes.Count.Pluralize("node"), @ss.Count().ToComma() @ss.Count().Pluralize("shard", includeNumber: false)@if (hs.InitializingShards > 0)
                                                                                                                                              {<text>, <b>@hs.InitializingShards.ToComma() initializing</b></text>}@if (hs.RelocatingShards > 0)
                                                                                                                                                                                                                   {<text>, <b>@hs.RelocatingShards.ToComma() relocating</b></text>}@if (hs.UnassignedShards > 0)
                                                                                                                                                                                                                                                                                    {<text>, <b>@hs.UnassignedShards.ToComma() unassigned</b></text>})@CommonVersionHeader(c)
                            </span>
                        </h3>
                    </th>
                </tr>
                <tr>
                    <th></th>
                    <th>Node</th>
                    <th>Shards</th>
                    <th>CPU</th>
                    <th>Memory</th>
                    <th>Virtual</th>
                    <th>Indexes</th>
                    <th>Searches</th>
                    <th>Conns</th>
                    <th>Rx</th>
                    <th>Tx</th>
                    <th>As of</th>
                </tr>
            </tbody>
            <tbody>
                @foreach (var n in ns.Nodes)
                {
                    // Don't show client nodes such as logstash inserters in this view
                    if (n.IsClient) { continue; }
                    var shards = ss.Where(sh => sh.Node == n.GUID);
                    var stats = n.Stats ?? new NodeStats();
                    var upTime = stats.JVM != null && stats.JVM.UptimeInMilliseconds > 0 ? DateTime.UtcNow.AddMilliseconds(-stats.JVM.UptimeInMilliseconds) : (DateTime?)null;
                    <tr class="@n.RowClass()">
                        <td>@n.IconSpan()</td>
                        <td title="Up since @(upTime.HasValue ? upTime.ToZuluTime() + " (" + upTime.ToRelativeTime() + ")" : "(unknown)")">
                            <a href="/elastic/node?cluster=@c.Name.UrlEncode()&node=@n.Name.UrlEncode()">@n.Name.TrimEnd("-" + c.Name)</a>
                        </td>
                        <td>@shards.Count().ToComma() (@shards.Count(s => s.Primary) primary)</td>
                        @if (stats.Process != null && stats.Process.CPU != null)
                        {
                            <td title="Process CPU: @stats.Process.CPU.Percent.ToComma()%
                                @if (stats.OS.LoadAverage != null)
                                { <text> load average @(string.Join(",", stats.OS.LoadAverage)) </text>    } ">
                                @((stats.OS.CPU.User + stats.OS.CPU.System).ToComma())%
                            </td>
                        }
                        else
                        {
                            <td class="note">n/a</td>
                        }
                        @if (stats.Process != null && stats.Process.Memory != null)
                        {
                            <td>@stats.Process.Memory.ResidentInBytes.ToSize("b")</td>
                            <td>@stats.Process.Memory.TotalVirtualInBytes.ToSize("b")</td>
                        }
                        else
                        {
                            <td class="note">n/a</td>
                            <td class="note">n/a</td>
                        }
                        @if (stats.Process != null)
                        {
                            <td title="@c.HealthStatus.Data.Indices.Count.ToComma() indexes with @(stats.Process.OpenFileDescriptors.ToComma()) open file descriptors">
                                <a href="/elastic/indices?cluster=@c.Name&node=@n.Name">
                                    @c.HealthStatus.Data.Indices.Count.ToComma()
                                </a> <span class="note">@(stats.Indices.Store.SizeInBytes > 0 ? "(" + stats.Indices.Store.SizeInBytes.ToSize("b") + ")" : "")</span>
                            </td>
                            <td>@stats.Indices.Search.QueryTotal.ToComma()</td>
                        }
                        else
                        {
                            <td class="note">n/a</td>
                            <td class="note">n/a</td>
                        }
                        @if (stats.HTTP != null)
                        {
                            <td title="@stats.HTTP.CurrentOpen open, @stats.HTTP.TotalOpened.ToComma() total">@stats.HTTP.CurrentOpen.ToComma()</td>
                        }
                        else
                        {
                            <td><span class="note">n/a</span></td>
                        }
                        @if (stats.Transport != null)
                        {
                            <td>@stats.Transport.RXSizeInBytes.ToSize("b")</td>
                            <td>@stats.Transport.TXSizeInBytes.ToSize("b")</td>
                        }
                        else
                        {
                            <td class="note">n/a</td>
                            <td class="note">n/a</td>
                        }
                        <td>@c.Status.ToPollSpan()</td>
                    </tr>
                }
                @if (c.SettingsNodes.Count > ns.Nodes.Count)
                {
                    <tr class="critical-row centered">
                        <td colspan="12">@((c.SettingsNodes.Count - ns.Nodes.Count).Pluralize("node")) currently missing of (@string.Join(", ", c.SettingsNodes.Select(s => s))).</td>
                    </tr>
                }
            </tbody>
        </table>
    }
</div>
    <div class="cluster-sub-detail">
        @if (Model.Clusters.Any(c => c.TroubledIndexes.Any()))
        {
            <div class="node-dashboard-separator"></div>
            @Html.Partial("Cluster.Indices", Model)
        }
        @if (Model.Clusters.Any(c => c.TroubledShards.Any()))
        {
            <div class="node-dashboard-separator"></div>
            @Html.Partial("Cluster.Shards", Model)
        }
    </div>